﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using Microsoft.Office.Interop.Word;
using Microsoft.Office.Tools.Ribbon;
using Utilities;

namespace WordAddin
{
    public partial class MyAddin
    {
        private Document activeDoc;
        private User _user = null;
        private User _receiver = null;


        private void MyAddin_Load(object sender, RibbonUIEventArgs e)
        {
            Microsoft.Office.Interop.Word.Application word = Globals.ThisAddIn.Application;
            activeDoc = word.ActiveDocument;
        }

        private void btnQuickEncrypt_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                string err = ValidateInput();
                if (!err.Equals(string.Empty))
                {
                    MessageBox.Show(err, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                //hash input pass
                SHA256 sha = SHA256.Create();
                byte[] arrPass = sha.ComputeHash(Encoding.ASCII.GetBytes(ebPass.Text));

                byte[] arrKey = null;
                //select algo
                SymmetricAlgorithm algo = null;
                switch (cbbAlgo.Text)
                {
                    case "DES":
                        algo = new DESCryptoServiceProvider();
                        arrKey = new byte[8];
                        CopyBuffer(arrPass, arrKey, 8);
                        break;
                    case "TripleDES":
                        algo = new TripleDESCryptoServiceProvider();
                        arrKey = new byte[24];
                        CopyBuffer(arrPass, arrKey, 24);
                        break;
                    case "RC2":
                        algo = new RC2CryptoServiceProvider();
                        arrKey = new byte[16];
                        CopyBuffer(arrPass, arrKey, 16);
                        break;
                    case "Rijndael":
                        algo = new RijndaelManaged();
                        arrKey = new byte[16];
                        CopyBuffer(arrPass, arrKey, 16);
                        break;
                }

                activeDoc.Content.Text = Utilities.EncryptionProvider.Encrypt(algo, activeDoc.Content.Text, arrKey, arrKey, PaddingMode.ANSIX923, CipherMode.CBC);

            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        private string ValidateInput()
        {
            if (ebPass.Text.Equals(string.Empty))
            {
                return "chưa nhập password!";
            }

            if (cbbAlgo.Text.Equals(string.Empty))
            {
                return "chưa chọn thuật toán mã hóa!";
            }

            return string.Empty;
        }

        private void CopyBuffer(byte[] src, byte[] des, int length)
        {
            if (src.Length < length)
                return;
            int i;
            for (i = 0; i < length; i++)
            {
                des[i] = src[i];
            }
        }

        private void btnQuickDecrypt_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                string err = ValidateInput();
                if (!err.Equals(string.Empty))
                {
                    MessageBox.Show(err, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                //hash input pass
                SHA256 sha = SHA256.Create();
                byte[] arrPass = sha.ComputeHash(Encoding.ASCII.GetBytes(ebPass.Text));

                byte[] arrKey = null;
                //select algo
                SymmetricAlgorithm algo = null;
                switch (cbbAlgo.Text)
                {
                    case "DES":
                        algo = new DESCryptoServiceProvider();
                        arrKey = new byte[8];
                        CopyBuffer(arrPass, arrKey, 8);
                        break;
                    case "TripleDES":
                        algo = new TripleDESCryptoServiceProvider();
                        arrKey = new byte[24];
                        CopyBuffer(arrPass, arrKey, 24);
                        break;
                    case "RC2":
                        algo = new RC2CryptoServiceProvider();
                        arrKey = new byte[16];
                        CopyBuffer(arrPass, arrKey, 16);
                        break;
                    case "Rijndael":
                        algo = new RijndaelManaged();
                        arrKey = new byte[16];
                        CopyBuffer(arrPass, arrKey, 16);
                        break;
                }

                activeDoc.Content.Text = EncryptionProvider.Decrypt(algo, activeDoc.Content.Text, arrKey, arrKey, PaddingMode.ANSIX923, CipherMode.CBC);

            }
            catch (System.Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        private void btnImportYourInfo_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                openFileDialog1.Title = "Import your xml file";
                openFileDialog1.Filter = "XML file (.xml) | *.xml";
                openFileDialog1.FileName = "";
                openFileDialog1.Multiselect = false;
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    this._user = this.ImportUserInfo(openFileDialog1.FileName);
                    if (this._user != null)
                    {
                        btnDecrypt.Enabled = true;
                        btnEncrypt.Enabled = true;
                        btnImportFrienInfo.Enabled = true;
                        btnSignFile.Enabled = true;
                        btnVerifySign.Enabled = true;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Utility.ExceptionTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private User ImportUserInfo(string xmlFile)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(User));
            TextReader reader = null;
            User user = new User();
            try
            {
                reader = new StreamReader(openFileDialog1.FileName);
                user = (User)serializer.Deserialize(reader);
                return user;
            }
            catch (Exception)
            {
                MessageBox.Show("Cannot load user database.", Utility.ExceptionTitle, MessageBoxButtons.OK, MessageBoxIcon.Stop);
                return null;
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }
        }

        private void btnImportFrienInfo_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                openFileDialog1.Title = "Import your xml file";
                openFileDialog1.Filter = "XML file (.xml) | *.xml";
                openFileDialog1.FileName = "";
                openFileDialog1.Multiselect = false;
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    this._receiver = this.ImportUserInfo(openFileDialog1.FileName);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Utility.ExceptionTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void btnEncrypt_Click(object sender, RibbonControlEventArgs e)
        {
            Encryptfrm encryptForm = new Encryptfrm(this._receiver);
            if (encryptForm.ShowDialog() == DialogResult.OK)
            {
                byte[] key = EncryptionProvider.GenerateKeyBytes(encryptForm.EncryptionAlgorithm);

                RSACryptoServiceProvider rsa = null;
                if (encryptForm.JustMe)
                {
                    rsa = _user.GetRSAKeyPair(false);
                }
                else
                {

                    rsa = _receiver.GetRSAKeyPair(false);
                }


                string content = activeDoc.Content.Text;
                content = content.Substring(0, content.Length - "\r".Length);

                Utility.WriteFile("encrypt.tmp", content);

                Stream ins = new StreamReader("encrypt.tmp").BaseStream;
                Stream outs = new StreamWriter(encryptForm.OutputPath, false).BaseStream;
                try
                {
                    EncryptionProvider.Encrypt(ins, outs,
                        encryptForm.EncryptionAlgorithm,
                        key,
                        rsa,
                        encryptForm.PaddingMode,
                        encryptForm.CipherMode);

                    MessageBox.Show("Mã hóa thành công! \n File đã mã hóa được lưu ở: " + encryptForm.OutputPath, Utility.InformTitle);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, Utility.ExceptionTitle);
                    return;
                }
                finally
                {
                    File.Delete("encrypt.tmp");
                }
            }
        }

        private void btnDecrypt_Click(object sender, RibbonControlEventArgs e)
        {
            ConfirmPassFrm frm = new ConfirmPassFrm(this._user);
            if (frm.ShowDialog() == DialogResult.OK)
            {
                openFileDialog1.Title = "Select file to decrypt ... ";
                openFileDialog1.FileName = string.Empty;
                openFileDialog1.Filter = "Encrypted file (.enc)|*.enc";
                openFileDialog1.Multiselect = false;
                if (openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    string filePath = openFileDialog1.FileName;
                    Stream enc = new StreamReader(filePath).BaseStream;

                    string outputPath = filePath.Remove(filePath.LastIndexOf('.')) + ".txt";

                    Stream dec = new StreamWriter(outputPath).BaseStream;

                    RSACryptoServiceProvider rsa = this._user.GetRSAKeyPair(true);
                    try
                    {
                        EncryptionProvider.Decrypt(enc, dec, rsa);

                        MessageBox.Show("Giải mã thành công!. \n File đã giải mã được lưu ở: " + outputPath, Utility.InformTitle);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, Utility.ExceptionTitle);
                        return;
                    }
                }
            }
        }

        private void btnSignFile_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                ConfirmPassFrm frm = new ConfirmPassFrm(this._user);
                if (frm.ShowDialog() == DialogResult.OK)
                {
                    saveFileDialog1.Title = "Save signed file ... ";
                    saveFileDialog1.FileName = string.Empty;
                    saveFileDialog1.Filter = "Signed file (.sig) | *.sig";
                    if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                    {
                        string content = activeDoc.Content.Text;
                        content = content.Substring(0, content.Length - "\r".Length);

                        Utility.WriteFile("sig.tmp", content);

                        Stream inStream = new StreamReader("sig.tmp").BaseStream;
                        Stream outStream = new StreamWriter(saveFileDialog1.FileName).BaseStream;
                        RSACryptoServiceProvider rsa = this._user.GetRSAKeyPair(true);
                        EncryptionProvider.Sign(inStream, outStream, rsa, Utility.HashAlgo);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Utility.ExceptionTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                File.Delete("sig.tmp");
            }
        }

        private void btnVerifySign_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                VerifySignatureFrm frm = new VerifySignatureFrm();
                if (frm.ShowDialog() == DialogResult.OK)
                {
                    //read word document
                    ApplicationClass wordApp = new ApplicationClass();

                    object file = frm.InputFilePath;
                    object nullobj = Missing.Value;
                    Document doc = wordApp.Documents.Open(ref file, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj, ref nullobj);
                    Document doc1 = wordApp.ActiveDocument;
                    string content = doc1.Content.Text;
                    content = content.Substring(0, content.Length - "\r".Length);

                    nullobj = Missing.Value;
                    wordApp.Documents.Close(ref nullobj, ref nullobj, ref nullobj);
                    wordApp.Quit(ref nullobj, ref nullobj, ref nullobj);

                    activeDoc.Activate();

                    //write to file
                    Utility.WriteFile("infile.tmp", content);

                    Stream inStream = new StreamReader("infile.tmp").BaseStream;
                    Stream signStream = new StreamReader(frm.SigFilePath).BaseStream;

                    RSACryptoServiceProvider rsa = this._user.GetRSAKeyPair(false);
                    if (EncryptionProvider.Verify(inStream, signStream, rsa, Utility.HashAlgo))
                    {
                        MessageBox.Show("Valid signature of " + this._user.ToString(), Utility.InformTitle);
                    }
                    else
                        MessageBox.Show("Invalid signature.", Utility.InformTitle);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Utility.ExceptionTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                File.Delete("infile.tmp");
            }
        }
    }
}
